package factory;

import java.util.HashMap;
import java.util.Map;


/**
 * This is a generic factory class (and my attempt at producing reusable 
 * code). This is a reusable factory class that has the functionality of
 * creating and holding on to new classes created. It uses Java reflection
 * to instantiate new objects, and then holds a reference to these 
 * objects so that they need only be created once. 
 * 
 * This factory class is reusable because it strips out any assumptions 
 * made in the GEDIVA project; it makes no reference to the GEDIVA project.
 * This class can be used in any factory application where there is a 
 * factory needed to create objects (specifically, objects without
 * constructors);
 * 
 * @author Michael Deng
 *
 * @param <T> Abstract handler for all new classes created
 */
public class Factory<T> {

    private String myPackagePath = "";
    private String myErrorMessage = "";
    private Map<String, T> myClassMap;

    /**
     * The constructor creates the new Map that holds newly created classes.
     * @param packagePath path to the package containing the classes
     * @param errorMessage error message to display if error on class instantiation
     */
    public Factory (String packagePath, String errorMessage) {
        myClassMap = new HashMap<String, T>();
        myPackagePath = packagePath + ".";
        myErrorMessage = errorMessage;
    }

    /**
     * Returns an instance of the class requested. Method first
     * checks to see if an instance already exists. It will create
     * a new instance of the class if one does not already exist.
     * @param className name of action class
     * @return
     */
    public T getObject (String className) {
        if (!myClassMap.containsKey(className)) {
            myClassMap.put(className, createObject(className));
        }
        return myClassMap.get(className);
    }

    /**
     * creates a new class according to the string given; if it does not
     * exist in the class map.
     * 
     * @param className name of the class to create
     * @return
     */
    @SuppressWarnings("unchecked")
    private T createObject (String className) {
        String fullClassPath = null;
        T newAction = null;

        try {
            fullClassPath = myPackagePath + className;
            newAction = (T) Class.forName(fullClassPath).newInstance();
        }
        catch (InstantiationException e) {
            throwRuntimeException();
        }
        catch (IllegalAccessException e) {
            throwRuntimeException();
        }
        catch (ClassNotFoundException e) {
            throwRuntimeException();
        }
        catch (IllegalArgumentException e) {
            throwRuntimeException();
        }
        catch (SecurityException e) {
            throwRuntimeException();
        }
        return newAction;
    }

    /**
     * This method will execute to handle all the exceptions thrown during class creation.
     */
    private void throwRuntimeException() {
        throw new RuntimeException(myErrorMessage);
    }
}


